package game.johan.engine;

/*
Copyright (C) 2013 Johan Ceuppens

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
import java.io.*;
import javax.imageio.ImageIO;
import java.awt.image.*;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.awt.*;
import javax.swing.*;

import java.awt.*;
import javax.swing.*;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.GraphicsEnvironment;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.Timer;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.GraphicsEnvironment;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.Timer;
/*
 *  This class provides a bitmap surface and a
 *  ByteBuffer, you can render it and draw bytebuffers
 *  and bitmaps on it
 * 
 *  The Bytebuffer _bb gets bitmapped on _canvas with draawbbOn,
 *
 *  The bitmap can be written on with drawOn methods
 */
class Surface
{
private BufferedImage _image;
private ByteBuffer _bb,_loadbb;
private Caretaker<String> _fileresourcecaretaker = new Caretaker<String>();

private int _surfacew = 0;
private int _surfaceh = 0;
private int _surfacex = 0;
private int _surfacey = 0;
private String fileprefixpics = "./pics/";

public Surface(int x, int y, int w, int h) {
	_surfacex = x;
	_surfacey = y;
	_surfacew = w;
	_surfaceh = h;
	init();
}

public Surface(int x, int y, int w, int h, BufferedImage b) {
	_surfacex = x;
	_surfacey = y;
	_surfacew = w;
	_surfaceh = h;
	initWithImage(b);
}

private ByteBuffer init() {
 // open image
try {
 File path = new File(fileprefixpics+"stub.png");
 _image = ImageIO.read(path);
_bb = ByteBuffer.allocate(_surfacew * _surfaceh * 4);
} catch (IOException e) {
	System.out.println("Message "+e.getMessage());
	e.printStackTrace();
}
 // get DataBufferBytes from Raster
 WritableRaster raster = _image.getRaster();
 DataBufferByte data   = (DataBufferByte) raster.getDataBuffer();

 byte[] bb = data.getData();
// _bb.put(bb,0,bb.length);

 return _bb;

}


private ByteBuffer initWithImage(BufferedImage img) {
    _image = img;
 // get DataBufferBytes from Raster
 WritableRaster raster = _image.getRaster();
 DataBufferByte data   = (DataBufferByte) raster.getDataBuffer();

_bb = ByteBuffer.allocate(_surfacew * _surfaceh * 4);

 byte[] bb = data.getData();
 _bb.put(bb,0,bb.length);

    return _bb;

}

public int getsurfacew() {
	return _surfacew;
}

public int getsurfaceh() {
	return _surfaceh;
}

public int getsurfacex() {
	return _surfacex;
}

public int getsurfacey() {
	return _surfacey;
}

public void draw(EngineBase base) {
	render(base);
}

public void render(EngineBase base) {
  synchronized (this) {
    try {

	class DrawMethodR extends
	DrawMethod {
        public void init(BufferedImage img, int xx, int yy) {
                x = xx; y = yy; i = img; 
	}
        public void draw(Graphics2D g2d, JPanel jp) {
		g2d.drawImage((BufferedImage)i, null, x, y);
	}
        };

	DrawMethodR dm = new DrawMethodR();
	dm.init(_image, _surfacex, _surfacey);
    	base.drawOnPanel(dm);

    } catch (Exception e) {
    	System.out.println("Message "+e.getMessage());
    	e.printStackTrace();
    }
  }
}

public void drawbbOn() {
/***
	BufferedImage originalImage = ImageIO.read(new File(
					"c:/darksouls.jpg"));
	// convert BufferedImage to byte array
	ByteArrayOutputStream baos = new ByteArrayOutputStream();
	ImageIO.write(originalImage, "jpg", baos);
	baos.flush();
	_bb = baos.toByteArray();
	baos.close();
***/
try {
	// convert byte array back to BufferedImage
	byte[] bytes = new byte[toImageSurface()];
	_bb.get(bytes, 0, bytes.length);
	InputStream in = new ByteArrayInputStream(bytes);
//	BufferedImage bImageFromConvert = ImageIO.read(in);
	_image = ImageIO.read(in);
//	ImageIO.write(bImageFromConvert, "jpg", new File(
//					"c:/new-darksouls.jpg"));
    } catch (Exception e) {
    	System.out.println("Message "+e.getMessage());
    	e.printStackTrace();
    }
}

public void drawloadbbOn() {
try {
	byte[] bytes = new byte[toImageSurface()];
	_loadbb.get(bytes, 0, bytes.length);
	InputStream in = new ByteArrayInputStream(bytes);
	_image = ImageIO.read(in);
    } catch (Exception e) {
    	System.out.println("Message "+e.getMessage());
    	e.printStackTrace();
    }
}

private void release() {
    _image = null;
    _bb = null;
    //_loadbb = null;
}


public byte[] toByteArray() {
	byte[] bytes = new byte[toImageSurface()];
	//try {
	    _bb.get(bytes, 0, bytes.length);
	//}
	//catch (BufferUnderflowException e)
	//{
    	//System.out.println("Message "+e.getMessage());
    	//e.printStackTrace();
	//}
	return bytes;
	//do something with bytes
}

public byte[] toArray2() {
	byte[] bytes = new byte[toImageSurface()];
	return bytes;
	//do something with bytes
}

public int toImageSurface() {
	//int size = _image.getRowBytes() * _image.getHeight();
	int size = _surfacew * _surfaceh * 4;
	_bb = ByteBuffer.allocate(size);
//FIXME	_image.copyPixelsToBuffer(_bb);
	return size;
}

/*
 * ww,hh is width and height of bb arg
 */
public void drawOnByteBuffer(int ww, int hh, ByteBuffer bb) {
	int sizew = _surfacew;//_image.width;
	int i = 0, j = 0, kw = 0;
	for ( ; i < hh; i++) {
		for( ; j < ww; j++) {
			_bb.put(kw,bb.get(i*ww+j));
			kw++;
		}
		kw += sizew-ww;
		j = 0;
	}
}
	
public void toByteBuffer(byte[] ba) {
	int ww = _surfacew;
	int hh = _surfaceh;
	int sizew = _surfacew;//FIXME!
	int i = 0, j = 0, kw = 0;
	for ( ; i < hh; i++) {
		for( ; j < ww; j++) {
			_bb.get(ba, 0, ba.length);
			kw++;
		}
		kw += sizew-ww;
		j = 0;
	}
}
	
/*
 * This draws the byte[] ba on the _image surface with offsets xx,yy and
 * width and height ww,hh.
 * The bitmap can be loaded with the load() method
 * and provide a background surface
 */
public void drawOnByteArray(int xx, int yy, int ww, int hh, byte[] ba) {
	toByteBuffer(ba);
	drawOnByteBufferxy(xx,yy,ww,hh,_bb);
	//done above _image.copyPixelsFromBuffer(_bb);			
}
	
/*
 * The following method write the bytebuffer after load()
 * over the surface _image
 * There also is a loadanddraw method in this file
 */
public void drawOnLoadByteBuffer(int xx, int yy, int ww, int hh) {
	drawOnByteBufferxy(xx,yy,ww,hh,_loadbb);
	//done above _image.copyPixelsFromBuffer(_bb);			
}

/*
 * ww,hh is width and height of bb arg
 * xx,yy is position of in bb stored image to draw on _bb
 */
public void drawOnByteBufferxy(int xx, int yy, int ww, int hh, ByteBuffer bb) {
	int sizew = _surfacew;
	//int sizeh = _image.getHeight();
	int i = 0, j = 0, kw = xx+yy*ww;
	for ( ; i < hh; i++) {
		for( ; j < ww; j++) {
			_bb.put(kw,bb.get(i*ww+j));
			kw++;
		}
		kw += sizew-ww;
		j = 0;
	}
	
	
}

/*
 * set the _image to a region of the _image
 */
public void setRegion(int regx, int regy, int regw, int regh)
{
 	WritableRaster raster = _image.getRaster();
 	DataBufferByte data   = (DataBufferByte) raster.getDataBuffer();

 	byte[] srcbytes = data.getData();
	int size = regw * regh * 3;

	byte[] destbytes = new byte[size];
	int sizew = _surfacew*3;
	int i = 0, j = 0, kw = 0;
	for ( ; i < regh; i++) {
		for( ; j < regw*3; j+=3) {
			destbytes[i*regw*3+j] = srcbytes[kw];
			destbytes[i*regw*3+j+1] = srcbytes[kw+1];
			destbytes[i*regw*3+j+2] = srcbytes[kw+2];
			kw+=3;
		}
		kw += sizew-regw*3;
		j = 0;
	}
    try {
	InputStream in = new ByteArrayInputStream(destbytes);
	_image = ImageIO.read(in);
    } catch (Exception e) {
    	System.out.println("Message "+e.getMessage());
    	e.printStackTrace();
    }
		
	//drawbbOn();
}
/*
 * The following method provides a seperate bytebuffer _loadbb
 * which can be overwritten on the main _image surface
 * with the drawOnByteBufferxy method (bb as an ByteBuffer argument)
 */

public void load(String gfxfilename)
{

	if (_loadbb != null)
		return;
	
	try {
		if (gfxfilename == "") { 
			Image image = ImageIO.read(new File(fileprefixpics+"stub.png"));
			_image = (BufferedImage) image;
		} else {
			Image image = ImageIO.read(new File(fileprefixpics+gfxfilename));
			_image = (BufferedImage) image;
		}
	} catch (Exception e) {
    		System.out.println("Message "+e.getMessage());
    		e.printStackTrace();
	}
}

public void reload(String gfxfilename)
{
	try {
		if (gfxfilename == "") { 
			Image image = ImageIO.read(new File(fileprefixpics+"stub.png"));
			_image = (BufferedImage) image;
		} else {
			Image image = ImageIO.read(new File(fileprefixpics+gfxfilename));
			_image = (BufferedImage) image;
		}
	} catch (Exception e) {
    	System.out.println("Message "+e.getMessage());
    	e.printStackTrace();
	}
}

public void loadAndDraw(String gfxfilename, int xx, int yy, int ww, int hh)
{
	load(gfxfilename);
	_fileresourcecaretaker.save(gfxfilename);
	drawOnLoadByteBuffer(xx,yy,ww,hh);
}

public void reloadAndDraw(String gfxfilename, int xx, int yy, int ww, int hh)
{
	reload(gfxfilename);
	_fileresourcecaretaker.save(gfxfilename);
	drawOnLoadByteBuffer(xx,yy,ww,hh);
}

public void setxy(int xx,int yy) {
	_surfacex = xx;
	_surfacey = yy;
}

public int getx() { return _surfacex; }
public int gety() { return _surfacey; }
}
